//
// Created by LiuYou on 2021/6/18.
//

/*
 * 我们要把数据结构隐藏到实现文件中, 不希望用户看到。
 */


#include <malloc.h>
#include <cerrno>
#include <unistd.h>
#include <algorithm>
#include <csignal>
#include <sys/time.h>
#include "MyTBF.hpp"


static struct mytbf_st* job[MYTBF_MAX];
static int inited = 0;

// TODO 注意: 这我这个环境中下面这一行代码不写也是可以的！
typedef void (* sighandler_t)(int);

//static sighandler_t alrm_handler_save;

static struct sigaction alrm_sa_save;

/**
 * @brief 令牌桶的具体实现。
 */
struct mytbf_st {
    int cps;
    int burst;
    int token;
    int pos;
};

static int get_free_pos() {
    for (int i = 0; i < MYTBF_MAX; ++i) {
        if (job[i] == nullptr) {
            return i;
        }
    }
    return -1;
}

/*static void alrm_handler(int s) {
    // TODO 注意: 改进
    //  如果当前的信号来源是kernel, 我就响应。
    //  只要不是kernel来的信号, 我一概不响应。


    alarm(1);
    for (int i = 0; i < MYTBF_MAX; ++i) {
        if (job[i] != nullptr) {
            job[i]->token += job[i]->cps;
            if (job[i]->token > job[i]->burst) {
                job[i]->token = job[i]->burst;
            }
        }
    }
}*/

static void alrm_action(int s, siginfo_t* infop, void* unused) {
    // TODO 注意: 改进
    //  如果当前的信号来源是kernel, 我就响应。
    //  只要不是kernel来的信号, 我一概不响应。
    if (infop->si_code != SI_KERNEL) {
        return;
    }


//    alarm(1);
    for (int i = 0; i < MYTBF_MAX; ++i) {
        if (job[i] != nullptr) {
            job[i]->token += job[i]->cps;
            if (job[i]->token > job[i]->burst) {
                job[i]->token = job[i]->burst;
            }
        }
    }
}


/**
 * @brief 作用: 发出第一个信号。
 */
static void module_load() {
//    alrm_handler_save = signal(SIGALRM, alrm_handler);
//    alarm(1);

    struct sigaction sa{};

    sa.sa_sigaction = alrm_action;
    sigemptyset(&sa.sa_mask);
    // TODO 注意: 表示信号的处理函数是三参数。
    sa.sa_flags = SA_SIGINFO;

    sigaction(SIGALRM, &sa, &alrm_sa_save);


    struct itimerval itv{};
    itv.it_interval.tv_sec = 1;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 0;

    setitimer(ITIMER_REAL, &itv, nullptr);


    // TODO 注意: 设计重点！
    //  使用钩子函数。
    std::atexit(module_load);
}


static void module_unload() {
//    signal(SIGALRM, alrm_handler_save);
//    alarm(0);

    sigaction(SIGALRM, &alrm_sa_save, nullptr);

    struct itimerval itv{};
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 0;
    itv.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL, &itv, nullptr);


    for (int i = 0; i < MYTBF_MAX; ++i) {
        // TODO 注意: free()函数如果参数是 void* , 那么
        //  free()函数不会出错, free()一个 void* 是没有什么影响的。
        free(job[i]);
    }
}

mytbf_t* mytbf_init(int cps, int burst) {
    // TODO 注意: 设计上的重点！
    if (!inited) {
//        signal(SIGALRM,);
//        alarm(1);
        // TODO 注意: 设计上的重点！
        module_load();
        inited = 1;
    }

    int pos = get_free_pos_unlocked();
    if (pos < 0) {
        return nullptr;
    }
    mytbf_st* me = static_cast<mytbf_st*>(malloc(sizeof(*me)));
    if (me == nullptr) {
        return nullptr;
    }
    me->token = 0;
    me->cps = cps;
    me->burst = burst;
    me->pos = pos;

    job[pos] = me;
    return me;
}

int mytbf_fetchtoken(mytbf_t* ptr, int size) {
    mytbf_st* me = static_cast<mytbf_st*>(ptr);
    if (size <= 0) {
        // TODO 注意: 巧妙使用了 cerrno 。
        return -EINVAL;
    }
    // TODO 注意: 是 while 而不是 if。
    while (me->token <= 0) {
        pause();
    }

    // TODO 注意: 这里是使用了C++的STL算法。
    //  并没有像慧琴老师那样自己实现了一个 min()函数。
    int minVal = std::min(me->token, size);
    me->token -= minVal;
    return minVal;
}

int mytbf_returntoken(mytbf_t* ptr, int size) {
    if (size <= 0) {
        return -EINVAL;
    }
    mytbf_st* me = static_cast<mytbf_st*>(ptr);
    me->token += size;
    if (me->token > me->burst) {
        me->token = me->burst;
    }
    return size;
}

int mytbf_destroy(mytbf_t* ptr) {
    mytbf_st* me = static_cast<mytbf_st*>(ptr);
    // TODO 注意: 设计上的重点！
    job[me->pos] = nullptr;
    free(ptr);

    return 0;
}
